﻿using System;

namespace Alderamin.Core
{

    /// <summary>
    /// Contains the values of request methods defined for HTTP
    /// </summary>
    public enum RequestMethod
    {
        /// <summary>
        /// The method indicates that the client requires the server to set up a transparent TCP/IP tunnel
        /// </summary>
        Connect,
        /// <summary>
        /// The method indicates that the client requests resource from the server
        /// </summary>
        Get,
        /// <summary>
        /// The method indicates that the client submits data to the server
        /// </summary>
        Post,
        /// <summary>
        /// The method indicates that the client uploads a representation of the specified resource to the server
        /// </summary>
        Put
    }

    /// <summary>
    /// Contains the values of common port numbers 
    /// </summary>
    public enum CommonPortNumber
    {
        /// <summary>
        /// The 80 port used by the server for listening HTTP requests
        /// </summary>
        HttpPort = 80,
        /// <summary>
        /// The 443 port used by the server for listening HTTPS requests with TLS/SSL
        /// </summary>
        HttpsPort = 443,
        /// <summary>
        /// The 21 port used by the server for listerning FTP requests
        /// </summary>
        FtpPort = 21
    }

    /// <summary>
    /// Contains the information of a web request
    /// </summary>
    public class RequestInfo
    {
        #region Public Properties
        /// <summary>
        /// Gets the method of request
        /// </summary>
        public RequestMethod Method { get; set; }

        /// <summary>
        /// Gets the host where the request to be sent
        /// </summary>
        public String RemoteHost { get; set; }

        /// <summary>
        /// Gets the port number listened by the remote server
        /// </summary>
        public Int32 RemotePort { get; set; }

        /// <summary>
        /// The uri of the requested resource
        /// </summary>
        public String RequestUri { get; set; }
        #endregion

        #region Public Methods
        /// <summary>
        /// Converts the source request data to a RequestInfo instance.
        /// A return value indicates whether the conversion succeeded.
        /// </summary>
        /// <param name="sourceData">The data to be parsed</param>
        /// <returns></returns>
        public static RequestInfo TryParse(string sourceData)
        {
            int lineEndIndex = sourceData.IndexOf("\r\n");
            string requestLine = null;
            if (lineEndIndex > 0)
            {
                requestLine = sourceData.Substring(0, lineEndIndex);
            }
            string[] tempArray = null;
            if (requestLine != null)
            {
                tempArray = requestLine.Split(' ');
            }
            // A vaild request line should contain the information of http method, request uri and the protocol version
            if (null == tempArray || tempArray.Length != 3)
            {
                return null;
            }

            // Get the http method of the request
            string methodString = tempArray[0];
            RequestMethod method;
            switch (methodString)
            {
                case "CONNECT": method = RequestMethod.Connect; break;
                case "POST": method = RequestMethod.Post; break;
                case "PUT": method = RequestMethod.Put; break;
                case "GET": method = RequestMethod.Get; break;
                // Return null if the method string can't be determined
                default: return null;
            }

            // Get the target host name and port number
            string requestUri = tempArray[1];
            string remoteHost = null;
            int remotePort = 0;
            // Check if the url specifies the remote port
            string[] hostPort = requestUri.Split(':');
            if (hostPort.Length > 1)
            {
                if (!Int32.TryParse(hostPort[hostPort.Length - 1], out remotePort))
                {
                    if (requestUri.Contains("https://"))
                    {
                        remotePort = (int)CommonPortNumber.HttpsPort;
                    }
                    else if (requestUri.Contains("ftp://"))
                    {
                        remotePort = (int)CommonPortNumber.FtpPort;
                    }
                    else
                    {
                        remotePort = (int)CommonPortNumber.HttpPort;
                    }
                }
                // Temporarily set the remote host
                remoteHost = hostPort[hostPort.Length - 2];
            }
            // Look for the "Host" filed in the request header, if exist, replace the remoteHost with the value of "Host" field
            int hostFieldStartIndex = sourceData.IndexOf("Host:");
            if (hostFieldStartIndex > 0)
            {
                int endIndex = sourceData.IndexOf("\r\n", hostFieldStartIndex);
                remoteHost = sourceData.Substring(hostFieldStartIndex + 5, endIndex - hostFieldStartIndex - 5).Trim();
            }

            return new RequestInfo { Method = method, RemoteHost = remoteHost, RemotePort = remotePort, RequestUri = requestUri };
        }
        #endregion
    }
}
